import os

from PySide6.QtCore import Qt, Slot, Signal, QModelIndex, QPoint
from PySide6.QtGui import QAction, QIcon
from PySide6.QtWidgets import (
    QSizePolicy,
    QMessageBox,
    QSpacerItem,
    QMenu,
    QHBoxLayout,
    QLabel,
    QPushButton,
    QLineEdit,
    QToolBar,
    QWidget,
    QSplitter,
    QTreeView,
    QVBoxLayout,
    QComboBox,
    QSpinBox,
    QFileDialog,
)
from PySide6.QtPdfWidgets import QPdfView
from PySide6.QtPdf import QPdfDocument, QPdfBookmarkModel
from pypdf import PdfReader, PdfWriter

from .Utils import R


class PdfWidget(QWidget):
    """继承 QWidget, 用于 Pdf 基本操作"""

    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self.parent = parent

        self.InitActions()
        self.InitUi()

        self.lastPage = 0
        self.filename = ""

    def OpenLast(self):
        if not self.filename:
            return
        self.pdfDocument.load(self.filename)
        self.pageSelector.setMaximum(self.pdfDocument.pageCount() - 1)
        self.PageSelected(self.lastPage)

    def InitUi(self):
        vbox = QVBoxLayout(self)
        self.setLayout(vbox)

        self.toolbar = QToolBar(self)
        actions = [
            self.actionOpen,
            self.actionPrevPage,
            self.actionNextPage,
            self.actionZoomOut,
            self.actionZoomIn,
            self.actionPrevView,
            self.actionNextView,
        ]
        self.toolbar.addActions(actions)
        vbox.addWidget(self.toolbar)

        self.splitter = QSplitter(self)

        self.treeBookmark = QTreeView(self.splitter)
        self.pdfDocument = QPdfDocument(self)
        self.bookmarkModel = QPdfBookmarkModel(self)
        self.bookmarkModel.setDocument(self.pdfDocument)
        self.treeBookmark.setModel(self.bookmarkModel)
        self.treeBookmark.activated.connect(self.BookmarkSelected)
        self.treeBookmark.setHeaderHidden(True)

        self.treeBookmark.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeBookmark.customContextMenuRequested.connect(
            self.ShowTreeBookmarkContextMenu
        )
        self.treeBookmark_ContextMenu = QMenu()
        self.treeBookmark_ContextMenu.addAction(self.actionDeleteBookmark)

        self.pdfView = QPdfView(self.splitter)
        self.pdfView.setPageMode(QPdfView.PageMode.MultiPage)
        self.pdfView.setZoomMode(QPdfView.ZoomMode.FitToWidth)
        self.pdfView.setDocument(self.pdfDocument)

        self.zoomSelector = ZoomSelector(self)
        self.zoomSelector.setMaximumWidth(150)
        self.toolbar.insertWidget(self.actionZoomIn, self.zoomSelector)
        self.zoomSelector.ZoomModeChanged.connect(self.pdfView.setZoomMode)
        self.zoomSelector.ZoomFactorChanged.connect(self.pdfView.setZoomFactor)
        self.zoomSelector.Reset()

        self.pageSelector = QSpinBox(self)
        self.toolbar.insertWidget(self.actionNextPage, self.pageSelector)
        self.pageSelector.valueChanged.connect(self.PageSelected)
        nav = self.pdfView.pageNavigator()
        nav.currentPageChanged.connect(self.pageSelector.setValue)

        self.pdfView.zoomFactorChanged.connect(self.zoomSelector.SetFactor)
        self.pdfView.setContextMenuPolicy(Qt.CustomContextMenu)

        self.bookmarkPos = QPoint()
        self.bookmarkWidget = AddBookmarkWidget()
        self.bookmarkWidget.AddBookmarkSignal.connect(self.OnAddBookmark)

        self.pdfView.customContextMenuRequested.connect(self.ShowPdfViewContextMenu)
        self.pdfView_ContextMenu = QMenu()
        self.pdfView_ContextMenu.addAction(self.actionAddBookmark)

        self.splitter.addWidget(self.treeBookmark)
        self.splitter.addWidget(self.pdfView)
        self.splitter.setSizes((160, 800))
        vbox.addWidget(self.splitter)

    def InitActions(self):
        self.actionOpen = QAction(
            QIcon(R.OpenPath), "&Open", self, shortcut="Ctrl+O", triggered=self.Open
        )
        self.actionNextView = QAction(
            QIcon(R.NextViewPath),
            "&Next View",
            self,
            shortcut="Ctrl+N",
            triggered=self.NextView,
        )
        self.actionNextPage = QAction(
            QIcon(R.NextPagePath),
            "N&ext Page",
            self,
            shortcut="Ctrl+E",
            triggered=self.NextPage,
        )
        self.actionPrevView = QAction(
            QIcon(R.PrevViewPath),
            "&Previous View",
            self,
            shortcut="Ctrl+P",
            triggered=self.PreviousView,
        )
        self.actionPrevPage = QAction(
            QIcon(R.PrevPagePath),
            "P&revious Page",
            self,
            shortcut="Ctrl+R",
            triggered=self.PreviousPage,
        )
        self.actionZoomIn = QAction(
            QIcon(R.ZoomInPath),
            "&Zoom In",
            self,
            shortcut="Ctrl++",
            triggered=self.ZoomIn,
        )
        self.actionZoomOut = QAction(
            QIcon(R.ZoomOutPath),
            "Zoom &Out",
            self,
            shortcut="Ctrl+-",
            triggered=self.ZoomOut,
        )

        self.actionAddBookmark = QAction(
            "&Add Bookmark", self, triggered=self.AddBookmark
        )
        self.actionDeleteBookmark = QAction(
            "&Delete All Bookmarks", self, triggered=self.DeleteAllBookmarks
        )

    @Slot()
    def DeleteAllBookmarks(self):
        index = self.treeBookmark.currentIndex()
        page = index.data(int(QPdfBookmarkModel.Role.Page))

        writer = PdfWriter()
        with open(self.filename, "rb") as f:
            reader = PdfReader(f)
            for p in range(len(reader.pages)):
                writer.add_page(reader.pages[p])

        with open('out.pdf', "wb") as f:
            if QMessageBox.question(
                self,
                "Delete Bookmark",
                f"Delete All Bookmarks? if choose Yes: please see out.pdf",
            ) == QMessageBox.StandardButton.Yes:
                print('Delete all bookmarks success. Please see out.pdf')
                writer.write(f)

        self.lastPage = page
        self.OpenLast()

    @Slot()
    def AddBookmark(self):
        self.bookmarkWidget.move(self.bookmarkPos)
        self.bookmarkWidget.Clear()
        self.bookmarkPageNumber = self.pdfView.pageNavigator().currentPage()
        self.bookmarkWidget.SetPage(self.bookmarkPageNumber)
        self.bookmarkWidget.show()

    @Slot(str, int)
    def OnAddBookmark(self, title, page):
        writer = PdfWriter()
        with open(self.filename, "rb") as f:
            writer.append(f)
            writer.add_outline_item(title=title, page_number=page, parent=None)
        with open(self.filename, "wb") as f:
            writer.write(f)
            QMessageBox.about(self, "Add Bookmark", f"Add Bookmark {title} Success.")

        self.lastPage = page
        self.OpenLast()

    @Slot(QPoint)
    def ShowPdfViewContextMenu(self, pos: QPoint):
        self.bookmarkPos = self.pdfView.mapToGlobal(pos)
        self.pdfView_ContextMenu.exec(self.bookmarkPos)

    @Slot(QPoint)
    def ShowTreeBookmarkContextMenu(self, pos: QPoint):
        self.treeBookmark_ContextMenu.exec(self.treeBookmark.mapToGlobal(pos))

    def ZoomIn(self):
        factor = self.pdfView.zoomFactor() * 1.2
        self.pdfView.setZoomFactor(factor)

    def ZoomOut(self):
        factor = self.pdfView.zoomFactor() / 1.2
        self.pdfView.setZoomFactor(factor)

    def Open(self):
        filename, _ = QFileDialog.getOpenFileName(
            self,
            "Choose PDF File",
            os.path.dirname(self.filename),
            "PDF File (*.pdf) ;; All File (*)",
        )
        if not filename:
            return
        self.filename = filename
        page = 0
        if self.parent.recentFiles.get(filename):
            page, factor = tuple(self.parent.recentFiles[filename])
            self.pdfView.setZoomFactor(factor)
        self.OpenFile(filename,page)

    def OpenFile(self, filename, page=0):
        self.pdfDocument.load(filename)
        self.PageSelected(page)
        self.pageSelector.setMaximum(self.pdfDocument.pageCount() - 1)
        self.filename = filename

    def PreviousView(self):
        self.pdfView.pageNavigator().back()

    def NextView(self):
        self.pdfView.pageNavigator().forward()

    def PreviousPage(self):
        nav = self.pdfView.pageNavigator()
        self.PageSelected(nav.currentPage()-1)

    def NextPage(self):
        nav = self.pdfView.pageNavigator()
        self.PageSelected(nav.currentPage()+1)

    @Slot(QModelIndex)
    def BookmarkSelected(self, index: QModelIndex):
        if not index.isValid():
            return
        page = index.data(int(QPdfBookmarkModel.Role.Page))
        self.PageSelected(page)

    @Slot(int)
    def PageSelected(self, page: int):
        self.parent.statusBar().showMessage(f'{page} / {self.pdfDocument.pageCount()-1}  Pages')
        if self.pageSelector.value() == page: return
        nav = self.pdfView.pageNavigator()
        nav.jump(page, QPoint(), nav.currentZoom())
        


class ZoomSelector(QComboBox):
    """继承 QComboBox, 用于缩放选择"""

    ZoomModeChanged = Signal(QPdfView.ZoomMode)
    ZoomFactorChanged = Signal(float)

    def __init__(self, parent):
        super().__init__(parent)
        self.setEditable(True)
        items = (
            "Fit Width",
            "Fit Page",
            "12%",
            "25%",
            "33%",
            "50%",
            "75%",
            "100%",
            "125%",
            "150%",
            "200%",
            "250%",
            "300%",
            "400%",
        )
        for item in items:
            self.addItem(item)
        self.currentTextChanged.connect(self.TextChanged)
        self.lineEdit().editingFinished.connect(self.EditFinished)

    @Slot(float)
    def SetFactor(self, factor):
        percent = int(factor * 100)
        self.setCurrentText(f"{percent}%")

    @Slot()
    def Reset(self):
        self.setCurrentIndex(7)

    @Slot(str)
    def TextChanged(self, text: str):
        if text == "Fit Width":
            self.ZoomModeChanged.emit(QPdfView.ZoomMode.FitToWidth)
        elif text == "Fit Page":
            self.ZoomModeChanged.emit(QPdfView.ZoomMode.FitInView)
        elif text.endswith("%"):
            zoom = int(text[:-1])
            factor = zoom / 100.0
            self.ZoomModeChanged.emit(QPdfView.ZoomMode.Custom)
            self.ZoomFactorChanged.emit(factor)

    @Slot()
    def EditFinished(self):
        self.TextChanged(self.lineEdit().text())


class AddBookmarkWidget(QWidget):
    """添加书签时调用"""

    AddBookmarkSignal = Signal(str, int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.resize(320, 60)

        self.InitUi()

        self.title = ""
        self.page = 0

    def InitUi(self):
        vbox = QVBoxLayout(self)
        self.setLayout(vbox)

        self.widget1 = QWidget(self)
        hbox = QHBoxLayout(self.widget1)
        label = QLabel("书签名称：")
        self.lineEdit_BookmarkTitle = QLineEdit(self)
        hbox.addWidget(label)
        hbox.addWidget(self.lineEdit_BookmarkTitle)
        vbox.addWidget(self.widget1)

        self.widget2 = QWidget(self)
        hbox2 = QHBoxLayout(self.widget2)
        label = QLabel("当前页数：")
        self.lineEdit_PageNumber = QLineEdit(self)
        self.lineEdit_PageNumber.setMaximumWidth(60)
        spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        hbox2.addWidget(label)
        hbox2.addWidget(self.lineEdit_PageNumber, alignment=Qt.AlignmentFlag.AlignLeft)
        hbox2.addItem(spacer)
        vbox.addWidget(self.widget2)

        self.widget3 = QWidget(self)
        hbox3 = QHBoxLayout(self.widget3)
        spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.pushButton_AddBookmark = QPushButton("&Add")
        self.pushButton_Cancel = QPushButton("&Cancel")
        hbox3.addItem(spacer)
        hbox3.addWidget(self.pushButton_AddBookmark)
        hbox3.addWidget(self.pushButton_Cancel)
        vbox.addWidget(self.widget3)

        self.pushButton_AddBookmark.clicked.connect(self.AddBookmark)
        self.pushButton_Cancel.clicked.connect(self.Cancel)

    def SetPage(self, page):
        self.lineEdit_PageNumber.setText(str(page))

    def AddBookmark(self):
        self.title = self.lineEdit_BookmarkTitle.text()
        if not self.title:
            QMessageBox.about(self, "Add Bookmark", "Title cannot be empty!")
            return
        self.page = int(self.lineEdit_PageNumber.text())
        self.AddBookmarkSignal.emit(self.title, self.page)
        print(f"AddBookmarkSignal: {self.title}, {self.page}")
        self.close()

    def Clear(self):
        self.lineEdit_BookmarkTitle.setText("")
        self.lineEdit_PageNumber.setText("")

    def Cancel(self):
        self.close()
